#include <stdint.h>
#include "zqm_global.h"
#include "hal_mcu.h"
#include "task_ota.h"

// iot ota data size and used blocks
static int ota_data_len = 0;
static int ota_data_block = 0;
static int ota_image_size = 0;

// 兼容传统Origin-tech的OTA协议和AKM 485协议
// Origin-tech的OTA协议: image_size = 0, 依赖外部call OtaEndAndRestart来结束OTA image的写入
// AKM 485协议: 依赖传入的image_size来判断写入结束，自主调用OtaEndAndRestart
void OtaBegin(int image_size)
{
    ota_data_len = 0;
    ota_data_block = 0;
    ota_image_size = image_size;
}
static void OtaWriteFlash(uint8_t *data, short size) // data Must be aligned to 4 bytes.
{
    // count offset
    int len = ota_data_len + size;
    // 超出干净区域，擦除
    for(int block_size = ota_data_block * EEPROM_BLOCK_SIZE; len > block_size; block_size += EEPROM_BLOCK_SIZE) {
        ota_data_block++;
        FLASH_ROM_ERASE(IMAGE_B_START_ADD + block_size, EEPROM_BLOCK_SIZE);
    }
    FLASH_ROM_WRITE(IMAGE_B_START_ADD + ota_data_len, data, size);
    ota_data_len += size;
    ZQLOG("IOT OTA Receive and write data, pkg size: %d, image size: %d", size, ota_data_len);
}
// ***注意***：582写flash要求数据4字节对齐, 同时每次写入4字节整数倍长度
// 所以：1. 传入的data之前的4字节(data-4)，也要求可以使用, 这样如果data不对其的时候, 本函数会自动利用data之前的4字节空间，搬运data数据，使其从4字节边缘开始
// 2. 如果size不满足4字节整数倍，则本函数会自动补齐，补齐部分会写入flash随机数据。
void OtaRecvData(uint8_t *data, short size)
{
    int offset = (size_t)(data) % 4;
    if (offset != 0) { // adjust data addr aligned to 4 bytes.
        memcpy(data - offset, data, size);
        data -= offset;
    }
    offset = size % 4; 
    if(offset != 0) // adjust size is an integer multiple of 4.
        size += 4 - offset;
    OtaWriteFlash(data, size);
}
uint8_t OtaCheckData(uint8_t checksum)
{
#ifdef ZQ_SIMULATOR
    extern uint8_t simu582_flash_data[];  // 448Ksimu582_flash_data
    return (ZqGetChecksum(simu582_flash_data + IMAGE_B_START_ADD, ota_data_len) == checksum);
#else
    return (ZqGetChecksum((uint8_t*)IMAGE_B_START_ADD, ota_data_len) == checksum);
#endif
}
void OtaEndAndRestart(void)
{
    ZQ_ALIGNED(4) unsigned int flag = IMAGE_IAP_FLAG;
    // 写入boot OTA flag
    EEPROM_ERASE(OTA_DATAFLASH_ADD, EEPROM_PAGE_SIZE);
    EEPROM_WRITE(OTA_DATAFLASH_ADD, (uint8_t*)&flag, 4);

    ZQLOG("IOT OTA reboot to update !");
    WWDG_ResetCfg(DISABLE); // 关看门狗准备执行OTA代码
    WdtClean();
    DelayMs(1000); // 等待一些操作，比如log输出完成
    unsigned long irqv;
    SYS_DisableAllIrq(&irqv); // 关中断。
    SYS_ResetExecute();  //软件复位MCU
}